package com.gagakuai.concurrent.locks;


import java.util.concurrent.ThreadLocalRandom;

/*
 * @program: settement
 * @description: *    管理线程状态（阻塞/唤醒线程）
 * *
 * *    阻塞/唤醒的语义在不同的场景下可以理解为生产/消费许可证，也可以理解为借出/归还许可证
 * *
 * *    标记为中断的线程无法阻塞（阻塞不起作用），但线程阻塞期间，可以对其设置中断状态
 * *
 * * 与wait、sleep、join这些阻塞不同的是，park期间设置中断标记不会触发中断异常
 * @author: houhong
 * @create: 2022-11-16 23:31
 */
public class LockSupport {

    private static final Unsafe U = Unsafe.getUnsafe();
    private static final long PARKBLOCKER = U.objectFieldOffset(Thread.class, "parkBlocker");
    private static final long SECONDARY = U.objectFieldOffset(Thread.class, "threadLocalRandomSecondarySeed");
    /*
     * Thread的属性
     */
    private static final long TID = U.objectFieldOffset(Thread.class, "tid");

    private LockSupport() {
    }

    /*
     * 等待消费一个许可证，这会使线程陷入阻塞。blocker参数仅作为线程阻塞标记。
     * <p>
     * 如果提前给过许可，则线程继续执行。
     * 如果陷入阻塞后等待许可，则可由别的线程发给它许可（在别的线程中调用unpark）。
     * 使用线程中断也可以唤醒陷入阻塞的线程。
     * <p>
     * 注：对标记为中断的线程使用阻塞无效
     */
    public static Object park(Object blocker) {

        Thread thread = Thread.currentThread();
        // 设置现成的阻塞标记，使得线程陷入阻塞
        setBlocker(thread, blocker);
        // 使线程一直陷入阻塞，直到被唤醒
        U.park(false, 0L);
        //使得线程脱离 阻塞状态
        setBlocker(thread, null);
        return null;
    }


    /*
     * 使线程阻塞nanos（单位：纳秒）时长后自动醒来（中途可被唤醒）
     * <p>
     * 注：对标记为中断的线程使用阻塞无效
     */
    public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            U.park(false, nanos);
            setBlocker(t, null);
        }
    }

    /*
     * 使线程陷入阻塞，直到deadline（以毫秒为单位的Unix时间戳）时间点时才醒来（中途可被唤醒）
     * <p>
     * 注：对标记为中断的线程使用阻塞无效
     */
    public static void parkUntil(Object blocker, long deadline) {

        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        U.park(true, deadline);
        setBlocker(t, null);
    }

    /*
     * 作用同park(blocker)方法，只是不设置阻塞标记
     */
    public static void park() {
        Thread thread = Thread.currentThread();
        U.park(false, 0L);
    }

    /*
     * 作用同parkNanos(blocker)方法，只是不设置阻塞标记
     */
    public static void parkNanos(long nacos) {
        if (nacos > 0) {
            U.park(false, nacos);
        }
    }

    public static void parkUntil(long deadline) {
        U.park(true, deadline);
    }

    public static void unpark(Thread thread) {

        if (thread != null) {
            U.unpark(thread);
        }
    }

    /*
     * 获取当前线程内的随机数生成器，并返回生成的种子
     */
    static final int nextSecondarySeed() {
        int r;
        Thread t = Thread.currentThread();
        if ((r = U.getInt(t, SECONDARY)) != 0) {
            r ^= r << 13;   // xorshift
            r ^= r >>> 17;
            r ^= r << 5;
        } else if ((r = ThreadLocalRandom.current().nextInt()) == 0) {
            r = 1; // avoid zero
        }
        U.putInt(t, SECONDARY, r);
        return r;
    }

    static final long getTid(Thread t) {
        return U.getLong(t, TID);
    }

    public static Object getBlocker(Thread t) {
        if (t == null) {
            throw new NullPointerException();
        }
        return U.getObjectVolatile(t, PARKBLOCKER);
    }

    /*
     * 设置线程的t的 "parkBlocker" 阻塞标志位为 arg
     */
    private static final void setBlocker(Thread t, Object arg) {

        U.putObject(t, PARKBLOCKER, arg);
    }
}